Operating systems typically partition computation into a kernel and a number of user-level or kernel-level software processes (or simply “processes”). In addition to many other functions, operating systems typically operate in the role of process manager. Under management of an operating system (OS), a process can have a finite lifetime and the execution of threads within a process can be bounded by that finite lifetime. In addition, operating systems typically provide mechanisms for enforcing isolation between processes. For example, most modern operating systems use virtual memory management hardware built into the processor to create a protected address space or protection domain for each process. To varying degrees, processes are isolated from each other because the operating system restricts their access to state or information inside other processes.
Lifetime management and state isolation of process is designed to allow a computer to perform concurrent operations (in the form of processes) while allowing developers to bound the complexity of a computation as a whole. The idea is that a programmer usually need be concerned only with the computation occurring within a single process—the one containing the programmer's program—because computations outside the process can have only limited effect on the process at hand. In the best case, the programmer can write a program for a single process' computation without concern for computation occurring in other processes, because they cannot affect the first process.
Unfortunately, a computer system typically includes many application programs that by their nature work together, so that the system's computation necessarily includes the interaction of multiple processes. For example, in a common configuration, the Microsoft® Office Outlook® communications and information management program uses at least two processes. One process is for composing individual email messages and another process is for displaying email folders, calendaring information, and the main graphical user interface (GUI). In another example, an operating system may use one process to manage the GUI desktop and other processes to manage logon requests or access to encrypted password-protected data. In many more instances, processes must share certain resources, such as the file store, or communicate with shared services, humans, etc.
Inter-Process Communication
The segregation of computation into distinct processes is helpful for structuring computational components. However, processes typically cannot be completely isolated, and this fact creates a need for communication mechanisms that allow processes to communicate with each other and with the components of the operating system, including the kernel itself. Collectively, these communication mechanisms are typically known by the term Inter-Process Communication (IPC).
Properly employing an IPC mechanism, multiple processes can with some degree of effort be programmed to share common data, but still without interfering with one another. IPC mechanisms may be grouped into two classes: shared-state mechanisms and message-passing mechanisms. Shared-state mechanisms include, but are not limited to, shared-memory and shared-synchronization mechanisms (like shared locks or semaphores). Message-passing mechanisms include, but are not limited to, the following: message queues, remote procedure calls (RPCs), local remote procedure calls (LRPCs), sockets, and named pipes. These IPC mechanisms are well known to those of ordinary skill in the art.
IPC mechanisms enable communication between processes while providing a framework for programmers to control the interaction between the computations occurring in separate processes. IPC mechanisms restrict the possible interactions so that programmers can consider fewer cases, or think about interactions more generally or more abstractly. These restrictions aid in the construction of correct, reliable software.
While conventional IPC approaches provide mechanisms to facilitate inter-process communication, they provide little (or no) structure to help programmers address or avoid possible problems associated with such communication. There is little (or no) assistance that helps ensure correct communication between processes without interfering with each other.
For example, when using shared-memory mechanisms, even a slight program error or a slight programmer misunderstanding can result in the contents of the shared memory being rendered incomprehensible by either process attempting to share the memory. While conventional OS message-passing mechanisms reduce the chance of actual state corruption between processes, they do nothing to prevent one process from presenting messages to another in an unexpected order or with unexpected contents, indirectly causing such corruption.
Errors involving miscommunication or unanticipated interactions between processes are common with conventional unstructured IPC approaches. Indeed, these types of errors occur frequently and are extremely expensive to detect, to understand, and to fix. They typically require time-consuming testing and debugging to detect their existence and extent, and find their cause and time-consuming development efforts to correct errors related to miscommunication or unanticipated interactions.